package ch.ethz.inf.vs.sunspot.driveronspot;

import org.json.me.JSONArray;
import org.json.me.JSONException;
import org.json.me.JSONObject;

/**
 * This class handles encoding and decoding of Sun SPOT representations
 * @author mayersi
 *
 */
public class SpotRepresentation {
    // Representation

    private JSONObject meAsJSON;
    private JSONObject oldMeAsJSON;
    
    // Sensors
    private short lightValue;
    private float temperatureValue;
    private short[][] ledColors;
    private boolean[] ledStatuses;
    private boolean[] switchStatuses;
    private float[] accelerationValues;
    private float[] tiltValues;
    
    // Actuators
    private short[][] ledColorFuture;
    private boolean[] ledStatusFuture;
    
    // Information
    private int interval;

    /**
     * Create a new Spot Representation and initialize its fields
     */
    public SpotRepresentation() {
        // Initialize Sensors
        this.lightValue = 0;
        this.temperatureValue = 0.0f;
        this.ledColors = new short[Constants.SPOT_NUMBER_AVAILABLE_LEDS][3];
        this.ledStatuses = new boolean[Constants.SPOT_NUMBER_AVAILABLE_LEDS];
        this.switchStatuses = new boolean[2];
        this.accelerationValues = new float[3];
        this.tiltValues = new float[3];

        // Initialize Actuators
        this.ledColorFuture = new short[Constants.SPOT_NUMBER_AVAILABLE_LEDS][3];
        this.ledStatusFuture = new boolean[Constants.SPOT_NUMBER_AVAILABLE_LEDS];

        this.interval = 0;

        meAsJSON = new JSONObject();
    }

    /**
     * This method encodes the current spot representation to a JSON String
     * @return the JSON-encoded spot representation
     */
    public String encodeJSON_OutToHost(int nextResyncTime) {
        if (oldMeAsJSON == null) {
            System.out.println("Created new OLDJSON object");
            oldMeAsJSON = new JSONObject();

            // We know the initial state of the LEDs!
            for (int i = 0; i < Constants.SPOT_NUMBER_AVAILABLE_LEDS; i++) {
                try {
                    JSONArray thisLED = new JSONArray();

                    thisLED.put("0");
                    for (int j = 0; j < 3; j++) {
                        thisLED.put("0");
                    }

                    oldMeAsJSON.put(Constants.SPOT_LED_NAME + i, thisLED);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }

        meAsJSON = new JSONObject();

        try {
            if (!oldMeAsJSON.has(Constants.SPOT_INTERVALFUTURE_NAME) || (oldMeAsJSON.getInt(Constants.SPOT_INTERVALFUTURE_NAME) != nextResyncTime)) {
                meAsJSON.put(Constants.SPOT_INTERVALFUTURE_NAME, nextResyncTime);
                oldMeAsJSON.put(Constants.SPOT_INTERVALFUTURE_NAME, nextResyncTime);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        try {
            if (!oldMeAsJSON.has(Constants.SPOT_LIGHT_NAME) || (getLightValue() != oldMeAsJSON.getInt(Constants.SPOT_LIGHT_NAME))) {
                meAsJSON.put(Constants.SPOT_LIGHT_NAME, "" + getLightValue());
                oldMeAsJSON.put(Constants.SPOT_LIGHT_NAME, "" + getLightValue());
            }

            if (!oldMeAsJSON.has(Constants.SPOT_TEMPERATURE_NAME) || (getTemperatureValue() != oldMeAsJSON.getDouble(Constants.SPOT_TEMPERATURE_NAME))) {
                meAsJSON.put(Constants.SPOT_TEMPERATURE_NAME, "" + getTemperatureValue());
                oldMeAsJSON.put(Constants.SPOT_TEMPERATURE_NAME, "" + getTemperatureValue());
            }

            for (int i = 0; i < Constants.SPOT_NUMBER_AVAILABLE_LEDS; i++) {
                JSONArray thisLED = new JSONArray();

                thisLED.put(this.ledStatuses[i] ? "1" : "0");
                for (int j = 0; j < 3; j++) {
                    thisLED.put("" + this.ledColors[i][j]);
                }

                if (!oldMeAsJSON.has(Constants.SPOT_LED_NAME + i) || !oldMeAsJSON.getJSONArray(Constants.SPOT_LED_NAME + i).toString().equalsIgnoreCase(thisLED.toString())) {
                    meAsJSON.put(Constants.SPOT_LED_NAME + i, thisLED);
                    oldMeAsJSON.put(Constants.SPOT_LED_NAME + i, thisLED);
                }
            }

            if (!oldMeAsJSON.has(Constants.SPOT_SWITCH_NAME + 0) || !((oldMeAsJSON.getInt(Constants.SPOT_SWITCH_NAME + 0) == 1) == this.switchStatuses[0])) {
                meAsJSON.put(Constants.SPOT_SWITCH_NAME + 0, this.switchStatuses[0] ? "1" : "0");
                oldMeAsJSON.put(Constants.SPOT_SWITCH_NAME + 0, this.switchStatuses[0] ? "1" : "0");
            }

            if (!oldMeAsJSON.has(Constants.SPOT_SWITCH_NAME + 1) || !((oldMeAsJSON.getInt(Constants.SPOT_SWITCH_NAME + 1) == 1) == this.switchStatuses[1])) {
                meAsJSON.put(Constants.SPOT_SWITCH_NAME + 1, this.switchStatuses[1] ? "1" : "0");
                oldMeAsJSON.put(Constants.SPOT_SWITCH_NAME + 1, this.switchStatuses[1] ? "1" : "0");
            }

            JSONArray thisAcceleration = new JSONArray();
            for (int i = 0; i < 3; i++) {
                thisAcceleration.put("" + this.accelerationValues[i]);
            }

            if (!oldMeAsJSON.has(Constants.SPOT_ACCELERATION_NAME) || !((oldMeAsJSON.getJSONArray(Constants.SPOT_ACCELERATION_NAME).toString().equalsIgnoreCase(thisAcceleration.toString())))) {
                meAsJSON.put(Constants.SPOT_ACCELERATION_NAME, thisAcceleration);
                oldMeAsJSON.put(Constants.SPOT_ACCELERATION_NAME, thisAcceleration);
            }

            JSONArray thisTilt = new JSONArray();
            for (int i = 0; i < 3; i++) {
                thisTilt.put("" + this.tiltValues[i]);
            }
             
            if (!oldMeAsJSON.has(Constants.SPOT_TILT_NAME) || !((oldMeAsJSON.getJSONArray(Constants.SPOT_TILT_NAME).toString().equalsIgnoreCase(thisTilt.toString())))) {
               
                meAsJSON.put(Constants.SPOT_TILT_NAME, thisTilt);
                oldMeAsJSON.put(Constants.SPOT_TILT_NAME, thisTilt);
            } 
        } catch (JSONException ex) {
            ex.printStackTrace();
        }

        // System.out.println("OUTTOHOST: " + meAsJSON.toString());
        return meAsJSON.toString();
    }

    /**
     * This method decodes the relevant parts of a JSON string to a spot representation
     * @param creator the creator string
     */
    public void decodeJSON_InFromHost(String creator) {
        // System.out.println("INFROMHOST: " + creator);
        if (creator.equalsIgnoreCase("")) {
            return;
        }

        try {
            meAsJSON = new JSONObject(creator);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        if (meAsJSON.has(Constants.SPOT_INTERVALFUTURE_NAME)) {
            try {
                setResyncTime(meAsJSON.getInt(Constants.SPOT_INTERVALFUTURE_NAME));
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        for (int i = 0; i < Constants.SPOT_NUMBER_AVAILABLE_LEDS; i++) {
            if (meAsJSON.has(Constants.SPOT_LEDFUTURE_NAME + i)) {
                try {
                    JSONArray leds = meAsJSON.getJSONArray(Constants.SPOT_LEDFUTURE_NAME + i);
                    this.ledStatusFuture[i] = (leds.getInt(0) == 1);

                    for (int j = 0; j < 3; j++) {
                        this.ledColorFuture[i][j] = (short) leds.getInt(j + 1);
                    }
                } catch (JSONException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    public void setResyncTime(int resyncTime) {
        this.interval = resyncTime;
    }

    public int getResyncTime() {
        return interval;
    }

    public short getLightValue() {
        return lightValue;
    }

    public void setLightValue(short lightValue) {
        this.lightValue = lightValue;
    }

    public double getTemperatureValue() {
        return temperatureValue;
    }

    public void setTemperatureValue(float temperatureValue) {
        this.temperatureValue = temperatureValue;
    }

    public boolean getLedStatus(int ledIndex) {
        return ledStatuses[ledIndex];
    }

    public void setLedStatus(int ledIndex, boolean ledStatus) {
        this.ledStatuses[ledIndex] = ledStatus;
    }

    public short[] getLedColor(int ledIndex) {
        return ledColors[ledIndex];
    }

    public void setLedColor(int ledIndex, short[] ledColors) {
        this.ledColors[ledIndex] = ledColors;
    }

    public boolean getSwitchStatus(int switchIndex) {
        return switchStatuses[switchIndex];
    }

    public void setSwitchStatus(int switchIndex, boolean switchStatus) {
        this.switchStatuses[switchIndex] = switchStatus;
    }

    public float[] getAccelerationValues() {
        return accelerationValues;
    }

    public void setAccelerationValues(float[] accelerationValues) {
        this.accelerationValues = accelerationValues;
    }

    public float[] getTiltValues() {
        return tiltValues;
    }

    public void setTiltValues(float[] tiltValues) {
        this.tiltValues = tiltValues;
    }
    
    public short[] getLedColorFuture(int ledIndex) {
        return ledColorFuture[ledIndex];
    }

    public void setLedColorFuture(int ledIndex, short[] ledColorFuture) {
        this.ledColorFuture[ledIndex] = ledColorFuture;
    }

    public boolean getLedStatusFuture(int ledIndex) {
        return ledStatusFuture[ledIndex];
    }

    public void setLedStatusFuture(int ledIndex, boolean ledStatusFuture) {
        this.ledStatusFuture[ledIndex] = ledStatusFuture;
    }
}
